package _case

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

func AtomicCase() {
	var count int64 = 5
	atomic.StoreInt64(&count, 10)
	fmt.Println(atomic.LoadInt64(&count))
	//累加 count += 10
	fmt.Println(atomic.AddInt64(&count, 10))
	//交换变量的值，返回原有的值 count = 20
	fmt.Println(atomic.SwapInt64(&count, 30))
	// count = 30
	fmt.Println(atomic.LoadInt64(&count))
	// 比较并替换原有的值，并返回是否替换成功，对比旧值，如果相等旧替换
	fmt.Println(atomic.CompareAndSwapInt64(&count, 50, 40))
	fmt.Println(atomic.CompareAndSwapInt64(&count, 30, 40))
}

type AtomicCounter struct {
	count int64
}

func (c *AtomicCounter) Inc() {
	atomic.AddInt64(&c.count, 1)
}
func (c *AtomicCounter) Load() int64 {
	return atomic.LoadInt64(&c.count)
}

// 不是原子操作导致 count 计算可能不等于 2000
func AtomicCase1() {
	var count int64 = 0
	wg := sync.WaitGroup{}
	for i := 0; i < 2000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			count++
		}()
	}
	wg.Wait()
	fmt.Println(count)
}

// 加锁
func AtomicCase2() {
	var count int64 = 0
	wg := sync.WaitGroup{}
	locker := sync.Mutex{}
	start := time.Now()
	for i := 0; i < 20000000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			locker.Lock()
			count++
			locker.Unlock()
		}()
	}
	wg.Wait()
	fmt.Println(count)
	fmt.Println("加锁时间：", time.Since(start))
}

func AtomicCase3() {
	var c = AtomicCounter{}

	wg := sync.WaitGroup{}
	start := time.Now()
	for i := 0; i < 20000000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			c.Inc()
		}()
	}
	wg.Wait()
	fmt.Println(c.Load())
	fmt.Println("atomic时间：", time.Since(start))
}

func AtomicCase4() {
	list := []string{"A", "B", "C", "D", "E", "F", "G"}
	mp := map[string]int{}
	// 定义一个原子值
	var atomicMap atomic.Value
	//将集合存储到原子值
	atomicMap.Store(&mp)
	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

		label:
			m := atomicMap.Load().(*map[string]int)
			m1 := map[string]int{}
			for k, item := range *m {
				m1[k] = item
			}

			for _, v := range list {
				_, ok := m1[v]
				if !ok {
					m1[v] = 0
				}
				m1[v] += 1
			}
			//替换不成功，重新执行替换
			swap := atomicMap.CompareAndSwap(m, &m1)
			if !swap {
				goto label
			}
		}()
	}
	wg.Wait()
	fmt.Println(atomicMap.Load())
}
